#include "mpp.h"

#include <stdlib.h>

#include "defines.h"
#include "mipi.h"

#define ALIGN_DOWN_SIZE 2

static int g_sMipiFd;
static VB_CONFIG_S g_stVbConfig = {0};
static SAMPLE_VI_CONFIG_S g_stViConfig = {0};
static SAMPLE_VPSS_CONFIG_S g_stVpssConfig = {0};
static SAMPLE_VO_CONFIG_S g_stVoConfig = {0};

int mpp_init() {
    HI_S32 s32Ret = 0;
    PIC_SIZE_E enPicSize;
    SIZE_S stSize;
    HI_U32 u32Framerate;

    // 获取传感器信息
    SAMPLE_COMM_VI_GetSensorInfo(&g_stViConfig);
    SAMPLE_COMM_VI_GetSizeBySensor(g_stViConfig.astViInfo[0].stSnsInfo.enSnsType, &enPicSize);
    SAMPLE_COMM_SYS_GetPicSize(enPicSize, &stSize);
    SAMPLE_COMM_VI_GetFrameRateBySensor(g_stViConfig.astViInfo[0].stSnsInfo.enSnsType, &u32Framerate);

    // 配置 VB
    g_stVbConfig.u32MaxPoolCnt = 2;
    // 获取一帧图片 buffer 的大小
    g_stVbConfig.astCommPool[0].u64BlkSize = COMMON_GetPicBufferSize(stSize.u32Width, stSize.u32Height,
        SAMPLE_PIXEL_FORMAT, DATA_BITWIDTH_8, COMPRESS_MODE_SEG, DEFAULT_ALIGN);
    g_stVbConfig.astCommPool[0].u32BlkCnt = 10;
    // 获取 raw buffer 的大小
    g_stVbConfig.astCommPool[1].u64BlkSize = VI_GetRawBufferSize(stSize.u32Width, stSize.u32Height,
        PIXEL_FORMAT_RGB_BAYER_16BPP, COMPRESS_MODE_NONE, DEFAULT_ALIGN);
    g_stVbConfig.astCommPool[1].u32BlkCnt = 4;

    // 配置 VI
    g_stViConfig.s32WorkingViNum = 1;
    g_stViConfig.as32WorkingViId[0] = 0;
    // 设置 VI 传感器信息
    g_stViConfig.astViInfo[0].stSnsInfo.MipiDev =
        SAMPLE_COMM_VI_GetComboDevBySensor(g_stViConfig.astViInfo[0].stSnsInfo.enSnsType, 0);
    g_stViConfig.astViInfo[0].stSnsInfo.s32BusId = 0;
    // 设置 VI 设备信息
    g_stViConfig.astViInfo[0].stDevInfo.ViDev = 0;
    g_stViConfig.astViInfo[0].stDevInfo.enWDRMode = WDR_MODE_NONE;
    // 设置 VI PIPE 信息
    g_stViConfig.astViInfo[0].stPipeInfo.aPipe[0] = 0;
    g_stViConfig.astViInfo[0].stPipeInfo.aPipe[1] = -1;
    g_stViConfig.astViInfo[0].stPipeInfo.aPipe[2] = -1;
    g_stViConfig.astViInfo[0].stPipeInfo.aPipe[3] = -1;
    g_stViConfig.astViInfo[0].stPipeInfo.enMastPipeMode = VI_OFFLINE_VPSS_OFFLINE;
    // 设置 VI 通道信息
    g_stViConfig.astViInfo[0].stChnInfo.ViChn = 0;
    g_stViConfig.astViInfo[0].stChnInfo.enPixFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    g_stViConfig.astViInfo[0].stChnInfo.enVideoFormat = VIDEO_FORMAT_LINEAR;
    g_stViConfig.astViInfo[0].stChnInfo.enDynamicRange = DYNAMIC_RANGE_SDR8;
    g_stViConfig.astViInfo[0].stChnInfo.enCompressMode = COMPRESS_MODE_SEG;

    // 配置 VPSS
    // 设置 VPSS 组
    g_stVpssConfig.grpId = 0;
    g_stVpssConfig.grpAttr.u32MaxW = stSize.u32Width;
    g_stVpssConfig.grpAttr.u32MaxH = stSize.u32Width;
    g_stVpssConfig.grpAttr.enPixelFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    g_stVpssConfig.grpAttr.enDynamicRange = DYNAMIC_RANGE_SDR8;
    g_stVpssConfig.grpAttr.stFrameRate.s32SrcFrameRate = -1;
    g_stVpssConfig.grpAttr.stFrameRate.s32DstFrameRate = -1;
    g_stVpssConfig.grpAttr.bNrEn = HI_TRUE;
    // 设置 VPSS 通道
    // 注意此处不能为通道 0, 因为 VPSS 通道 0 只支持放大, 其它通道只支持缩小
    g_stVpssConfig.chnEnable[1] = HI_TRUE;
    g_stVpssConfig.chnAttrs[1].u32Width = 1920;
    g_stVpssConfig.chnAttrs[1].u32Height = 1080;
    g_stVpssConfig.chnAttrs[1].enChnMode = VPSS_CHN_MODE_USER;
    g_stVpssConfig.chnAttrs[1].enVideoFormat = VIDEO_FORMAT_LINEAR;
    g_stVpssConfig.chnAttrs[1].enPixelFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    g_stVpssConfig.chnAttrs[1].enDynamicRange = DYNAMIC_RANGE_SDR8;
    g_stVpssConfig.chnAttrs[1].enCompressMode = COMPRESS_MODE_NONE;
    g_stVpssConfig.chnAttrs[1].stFrameRate.s32SrcFrameRate = -1;
    g_stVpssConfig.chnAttrs[1].stFrameRate.s32DstFrameRate = -1;
    g_stVpssConfig.chnAttrs[1].u32Depth = 2;
    g_stVpssConfig.chnAttrs[1].bMirror = HI_FALSE;
    g_stVpssConfig.chnAttrs[1].bFlip = HI_FALSE;
    g_stVpssConfig.chnAttrs[1].stAspectRatio.enMode = ASPECT_RATIO_NONE;

    // 配置 VO
    HI_S32 s32VoFramerate = 60;
    SAMPLE_COMM_VO_GetDefConfig(&g_stVoConfig);
    g_stVoConfig.enVoIntfType = VO_INTF_MIPI;
    g_stVoConfig.enIntfSync = VO_OUTPUT_USER;
    g_stVoConfig.enPicSize = PIC_D1_MIPI; // 800 * 480
    g_stVoConfig.stDispRect.s32X = 0;
    g_stVoConfig.stDispRect.s32Y = 0;
    // 注意未旋转时是竖屏, 所以分辨率应该是 480 * 800
    g_stVoConfig.stDispRect.u32Width = g_stVoConfig.stImageSize.u32Width = 480;
    g_stVoConfig.stDispRect.u32Height = g_stVoConfig.stImageSize.u32Height = 800;
    g_stVoConfig.enDstDynamicRange = DYNAMIC_RANGE_SDR8;

    // 初始化 VB 和 MPI 系统
    s32Ret = SAMPLE_COMM_SYS_Init(&g_stVbConfig);
    if (s32Ret != HI_SUCCESS) {
        LOG("SAMPLE_COMM_SYS_Init failed with %x\n", s32Ret);
        goto exit;
    }

    // 配置 MIPI LCD, 获取 MIPI 设备
    s32Ret = SAMPLE_VO_CONFIG_MIPI(&g_sMipiFd);
    if (s32Ret != HI_SUCCESS) {
        LOG("SAMPLE_VO_CONFIG_MIPI failed with %x\n", s32Ret);
        goto exit1;
    }

    // 启动 VI
    ISP_CTRL_PARAM_S stIspCtrlParam = {0};
    memset(&stIspCtrlParam, 0, sizeof(stIspCtrlParam));
    SAMPLE_COMM_VI_SetParam(&g_stViConfig);
    HI_MPI_ISP_GetCtrlParam(g_stViConfig.astViInfo[0].stPipeInfo.aPipe[0], &stIspCtrlParam);
    stIspCtrlParam.u32StatIntvl = u32Framerate / 30;
    HI_MPI_ISP_SetCtrlParam(g_stViConfig.astViInfo[0].stPipeInfo.aPipe[0], &stIspCtrlParam);
    s32Ret = SAMPLE_COMM_VI_StartVi(&g_stViConfig);
    if (s32Ret != HI_SUCCESS) {
        LOG("SAMPLE_COMM_VI_StartVi failed with %x\n", s32Ret);
        goto exit2;
    }

    // 启动 VPSS
    HI_MPI_VPSS_CreateGrp(g_stVpssConfig.grpId, &g_stVpssConfig.grpAttr);
    for (int i = 0; i < VPSS_MAX_PHY_CHN_NUM; i++) {
        if (g_stVpssConfig.chnEnable[i]) {
            HI_MPI_VPSS_SetChnAttr(g_stVpssConfig.grpId, i, &g_stVpssConfig.chnAttrs[i]);
            HI_MPI_VPSS_EnableChn(g_stVpssConfig.grpId, i);
        }
    }
    s32Ret = HI_MPI_VPSS_StartGrp(g_stVpssConfig.grpId);
    if (s32Ret != HI_SUCCESS) {
        LOG("HI_MPI_VPSS_StartGrp failed with %x\n", s32Ret);
        goto exit3;
    }

    // 绑定 VI->VPSS
    s32Ret = SAMPLE_COMM_VI_Bind_VPSS(g_stViConfig.astViInfo[0].stPipeInfo.aPipe[0],
                g_stViConfig.astViInfo[0].stChnInfo.ViChn, g_stVpssConfig.grpId);
    if (s32Ret != HI_SUCCESS) {
        LOG("SAMPLE_COMM_VI_Bind_VPSS failed with %x\n", s32Ret);
        goto exit4;
    }

    // 启动 VO 到 MIPI LCD 通路
    // 设置视频输出设备的公共属性
    VO_PUB_ATTR_S stVoPubAttr = {0};
    memset(&stVoPubAttr, 0, sizeof(stVoPubAttr));
    stVoPubAttr.enIntfType = g_stVoConfig.enVoIntfType;
    stVoPubAttr.enIntfSync = g_stVoConfig.enIntfSync;
    stVoPubAttr.stSyncInfo.bSynm = 0;
    stVoPubAttr.stSyncInfo.bIop = 1;
    stVoPubAttr.stSyncInfo.u8Intfb = 0;
    stVoPubAttr.stSyncInfo.u16Hmid = 1;
    stVoPubAttr.stSyncInfo.u16Bvact = 1;
    stVoPubAttr.stSyncInfo.u16Bvbb = 1;
    stVoPubAttr.stSyncInfo.u16Bvfb = 1;
    stVoPubAttr.stSyncInfo.bIdv = 0;
    stVoPubAttr.stSyncInfo.bIhs = 0;
    stVoPubAttr.stSyncInfo.bIvs = 0;
    stVoPubAttr.stSyncInfo.u16Hact = 480; // 480: Horizontal effective area. Unit: pixel
    stVoPubAttr.stSyncInfo.u16Hbb = 60; // 60: Horizontal blanking of the rear shoulder. Unit: pixel
    stVoPubAttr.stSyncInfo.u16Hfb = 50; // 50: Horizontal blanking of the front shoulder. Unit: pixel
    stVoPubAttr.stSyncInfo.u16Hpw = 10; // 10: The width of the horizontal sync signal. Unit: pixel
    stVoPubAttr.stSyncInfo.u16Vact = 800; // 800: Vertical effective area. Unit: line
    stVoPubAttr.stSyncInfo.u16Vbb = 24; // 24: Vertical blanking of the rear shoulder. Unit: line
    stVoPubAttr.stSyncInfo.u16Vfb = 20; // 20: Vertical blanking of the front shoulder. Unit: line
    stVoPubAttr.stSyncInfo.u16Vpw = 4; // 4: The width of the vertical sync signal. Unit: line
    stVoPubAttr.u32BgColor = g_stVoConfig.u32BgColor;
    HI_MPI_VO_SetPubAttr(g_stVoConfig.VoDev, &stVoPubAttr);
    HI_MPI_VO_SetDevFrameRate(g_stVoConfig.VoDev, s32VoFramerate);

    // 设置用户接口时序信息, 用于配置时钟源, 时钟大小和时钟分频比
    VO_USER_INTFSYNC_INFO_S stUserInfo = {0};
    memset(&stUserInfo, 0, sizeof(stUserInfo));
    stUserInfo.bClkReverse = HI_TRUE;
    stUserInfo.u32DevDiv = 1;
    stUserInfo.u32PreDiv = 1;
    stUserInfo.stUserIntfSyncAttr.enClkSource = VO_CLK_SOURCE_PLL;
    stUserInfo.stUserIntfSyncAttr.stUserSyncPll.u32Fbdiv = 244; // 244: PLL integer frequency multiplier coefficient
    stUserInfo.stUserIntfSyncAttr.stUserSyncPll.u32Frac = 0x1A36;
    stUserInfo.stUserIntfSyncAttr.stUserSyncPll.u32Refdiv = 4; // 4: PLL reference clock frequency division coefficient
    stUserInfo.stUserIntfSyncAttr.stUserSyncPll.u32Postdiv1 = 7; // 7: PLL first stage output frequency division coefficient
    stUserInfo.stUserIntfSyncAttr.stUserSyncPll.u32Postdiv2 = 7; // 7: PLL second stage output frequency division coefficient
    HI_MPI_VO_SetUserIntfSyncInfo(g_stVoConfig.VoDev, &stUserInfo);

    // 启动 VO
    s32Ret = HI_MPI_VO_Enable(g_stVoConfig.VoDev);
    if (s32Ret != HI_SUCCESS) {
        LOG("HI_MPI_VO_Enable failed with %x\n", s32Ret);
        goto exit5;
    }

    // 启动 VO layer
    VO_VIDEO_LAYER_ATTR_S stLayerAttr = {0};
    memset(&stLayerAttr, 0, sizeof(stLayerAttr));
    memcpy_s(&stLayerAttr.stDispRect, sizeof(RECT_S), &g_stVoConfig.stDispRect, sizeof(RECT_S));
    memcpy_s(&stLayerAttr.stImageSize, sizeof(SIZE_S), &g_stVoConfig.stImageSize, sizeof(SIZE_S));
    stLayerAttr.u32DispFrmRt = s32VoFramerate;
    stLayerAttr.enPixFormat = g_stVoConfig.enPixFormat;
    stLayerAttr.bDoubleFrame = HI_FALSE;
    stLayerAttr.bClusterMode = HI_FALSE;
    stLayerAttr.enDstDynamicRange = g_stVoConfig.enDstDynamicRange;
    if (g_stVoConfig.u32DisBufLen) {
        HI_MPI_VO_SetDisplayBufLen(g_stVoConfig.VoDev, g_stVoConfig.u32DisBufLen);
    }
    if (g_stVoConfig.enVoPartMode == VO_PART_MODE_MULTI) {
        HI_MPI_VO_SetVideoLayerPartitionMode(g_stVoConfig.VoDev, g_stVoConfig.enVoPartMode);
    }
    s32Ret = SAMPLE_COMM_VO_StartLayer(g_stVoConfig.VoDev, &stLayerAttr);
    if (s32Ret != HI_SUCCESS) {
        goto exit6;
    }
    // 设置视频层输出图像效果
    VO_CSC_S stVideoCSC = {0};
    memset(&stVideoCSC, 0, sizeof(stVideoCSC));
    if (g_stVoConfig.enVoIntfType == VO_INTF_MIPI) {
        HI_MPI_VO_GetVideoLayerCSC(g_stVoConfig.VoDev, &stVideoCSC);
        stVideoCSC.enCscMatrix = VO_CSC_MATRIX_BT709_TO_RGB_PC;
        HI_MPI_VO_SetVideoLayerCSC(g_stVoConfig.VoDev, &stVideoCSC);
    }

    // 启动 VO 通道
    VO_CHN_ATTR_S stChnAttr = {0};
    memset(&stChnAttr, 0, sizeof(stChnAttr));
    if (g_stVoConfig.enVoMode == VO_MODE_1MUX) {
        stChnAttr.stRect.s32X      = HI_ALIGN_DOWN(0, ALIGN_DOWN_SIZE);
        stChnAttr.stRect.s32Y      = HI_ALIGN_DOWN(0, ALIGN_DOWN_SIZE);
        stChnAttr.stRect.u32Width  = HI_ALIGN_DOWN(stLayerAttr.stImageSize.u32Width, ALIGN_DOWN_SIZE);
        stChnAttr.stRect.u32Height = HI_ALIGN_DOWN(stLayerAttr.stImageSize.u32Height, ALIGN_DOWN_SIZE);
        stChnAttr.u32Priority      = 0;
        stChnAttr.bDeflicker       = HI_FALSE;
    } else {
        LOG("Error: not support vo mode mux: %d\n", g_stVoConfig.enVoMode);
        goto exit7;
    }
    HI_MPI_VO_SetChnAttr(g_stVoConfig.VoDev, 0, &stChnAttr);
    HI_MPI_VO_SetChnRotation(g_stVoConfig.VoDev, 0, ROTATION_90);
    s32Ret = HI_MPI_VO_EnableChn(g_stVoConfig.VoDev, 0);
    if (s32Ret != HI_SUCCESS) {
        LOG("HI_MPI_VO_EnableChn failed with %x\n", s32Ret);
        goto exit7;
    }

    // 绑定 VPSS->VO
    // s32Ret = SAMPLE_COMM_VPSS_Bind_VO(g_stVpssConfig.grpId, 1, g_stVoConfig.VoDev, 0);
    // if (s32Ret != HI_SUCCESS) {
    //     LOG("SAMPLE_COMM_VPSS_Bind_VO failed with %x\n", s32Ret);
    //     goto exit8;
    // }

    return s32Ret;

exit9:
    // SAMPLE_COMM_VPSS_UnBind_VO(g_stVpssConfig.grpId, 1, g_stVoConfig.VoDev, 0);
exit8:
    SAMPLE_COMM_VO_StopChn(g_stVoConfig.VoDev, g_stVoConfig.enVoMode);
exit7:
    SAMPLE_COMM_VO_StopLayer(g_stVoConfig.VoDev);
exit6:
    SAMPLE_COMM_VO_StopDev(g_stVoConfig.VoDev);
exit5:
    SAMPLE_COMM_VI_UnBind_VPSS(g_stViConfig.astViInfo[0].stPipeInfo.aPipe[0], g_stViConfig.astViInfo[0].stChnInfo.ViChn, 0);
exit4:
    HI_MPI_VPSS_StopGrp(g_stVpssConfig.grpId);
exit3:
    for (int i = 0; i < VPSS_MAX_PHY_CHN_NUM; i++) {
        if (g_stVpssConfig.chnEnable[i]) {
            HI_MPI_VPSS_DisableChn(g_stVpssConfig.grpId, i);
        }
    }
    HI_MPI_VPSS_DestroyGrp(g_stVpssConfig.grpId);
    SAMPLE_COMM_VI_StopVi(&g_stViConfig);
exit2:
    SAMPLE_VO_DISABLE_MIPITx(g_sMipiFd);
    SampleCloseMipiTxFd(g_sMipiFd);
exit1:
    SAMPLE_COMM_SYS_Exit();
exit:
    return s32Ret;
}

void mpp_exit() {
    // SAMPLE_COMM_VPSS_UnBind_VO(g_stVpssConfig.grpId, 1, g_stVoConfig.VoDev, 0);
    SAMPLE_COMM_VO_StopVO(&g_stVoConfig);
    SAMPLE_COMM_VI_UnBind_VPSS(g_stViConfig.astViInfo[0].stPipeInfo.aPipe[0], g_stViConfig.astViInfo[0].stChnInfo.ViChn, 0);
    SAMPLE_COMM_VPSS_Stop(g_stVpssConfig.grpId, g_stVpssConfig.chnEnable);
    SAMPLE_COMM_VI_StopVi(&g_stViConfig);
    SAMPLE_VO_DISABLE_MIPITx(g_sMipiFd);
    SampleCloseMipiTxFd(g_sMipiFd);
    SAMPLE_COMM_SYS_Exit();
}
